package com.athena.framework.annotation;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.athena.framework.beanfactory.BeanFactory;
import com.athena.framework.beanfactory.DefaultBeanFactory;
import com.athena.framework.exception.BeanException;
import com.athena.framework.url.info.UrlInfo;
import com.athena.framework.url.info.UrlInfoFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Set;

/**
 * 解析Controller注解
 */
public class ControllerParsing implements AnnotationParsing {

    private final static Logger logger = LoggerFactory.getLogger(ControllerParsing.class);

    private UrlInfoFactory urlInfoFactory = UrlInfoFactory.creatUrlInfoFactory();

    private BeanFactory beanFactory = DefaultBeanFactory.creatBeanFactory();

    /**
     * 开始解析注解
     */
    @Override
    public void parsing() {
        //所有添加有Controller的类
        Set<Class<?>> classes = findAllScanPackgeByAnnotation(Controller.class);
        classes.stream().forEach(clazz -> parsingClass(clazz));
    }

    /**
     * 解析添加有controller的类
     *
     * @param clazz
     */
    private void parsingClass(Class<?> clazz) {
        //查找改类型再=在beanFactory中是否已经被托管
        List<?> beanClassList = beanFactory.findBeanByClass(clazz);
        //框架没有托管该类型的实例，那么先创建一个实例并托管
        if (CollUtil.isEmpty(beanClassList)) {
            //先把controller创建实例并保存
            UrlInfo controllerInfo = null;
            try {
                controllerInfo = saveController(clazz.newInstance());
            } catch (Exception e) {
                String msg = "创建实例：" + clazz.getName() + "失败！！！\r\n" + e.getMessage();
                logger.error(msg);
                throw new BeanException(msg);
            }
            //解析方法
            parsingMethod(clazz, controllerInfo);
        } else {
            beanClassList.forEach(cls -> {
                UrlInfo urlInfo = saveController(cls);
                parsingMethod(cls.getClass(), urlInfo);
            });
        }
    }

    /**
     * 解析并保存方法
     *
     * @param clazz
     * @param controllerInfo
     */
    private void parsingMethod(Class<?> clazz, UrlInfo controllerInfo) {
        //获取controller中的所有方法
        Method[] methods = ReflectUtil.getMethods(clazz);
        for (int i = 0; i < methods.length; i++) {
            saveMethod(controllerInfo, methods[i]);
        }
    }

    /**
     * 如果方法添加有UrlMapping注解，那么就将这个方法和url信息保存起来
     *
     * @param controller
     * @param method
     */
    private void saveMethod(UrlInfo controller, Method method) {
        UrlMapping urlMapping = method.getAnnotation(UrlMapping.class);
        if (urlMapping != null) {
            UrlInfo urlInfo = new UrlInfo();
            urlInfo.setExecutor(method);
            urlInfo.setParent(controller.getExecutor());
            urlInfo.setType("method");
            urlInfo.setDesc(urlMapping.desc());
            urlInfo.setKey(controller.getExecutor().getClass().getName() + "." + method.getName());
            urlInfo.setMethod(urlMapping.method());

            //拼接controller和methon的URL
            String url = controller.getUrl();
            if (!StrUtil.isEmpty(urlMapping.value())) {
                if (StrUtil.startWith(urlMapping.value(), "/")) {
                    url += urlMapping.value();
                } else {
                    url = url + "/" + urlMapping.value();
                }
            }
            urlInfo.setUrl(url);
            urlInfoFactory.addUrlInfo(urlInfo);

            if (logger.isInfoEnabled()){
                logger.info("找到url映射："+url);
            }
        }
    }

    /**
     * 创建controller实例并保存
     *
     * @param instance
     * @return 返回url信息
     */
    private UrlInfo saveController(Object instance) {
        beanFactory.addBean(instance);
        UrlInfo urlInfo = new UrlInfo();
        urlInfo.setKey(instance.getClass().getName());
        //获取controller注解中的描述
        Controller controller = instance.getClass().getAnnotation(Controller.class);
        urlInfo.setDesc(controller.desc());
        //获取UrlMapping注解中的描述
        UrlMapping urlMapping = instance.getClass().getAnnotation(UrlMapping.class);
        if (urlMapping != null) {
            if (!StrUtil.isBlank(urlMapping.desc())) {
                urlInfo.setDesc(urlMapping.desc());
            }
            urlInfo.setMethod(urlMapping.method());
            urlInfo.setUrl(urlMapping.value());
        }
        urlInfo.setType("class");
        urlInfo.setExecutor(instance);
        urlInfoFactory.addUrlInfo(urlInfo);
        return urlInfo;
    }
}
